//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////


#include "precomp.h"

DWORD g_dwLastSMSCmd = 0;

//
// Broadcast message languages
//
static const BYTE g_rgbBroadcastLangs[] =
{
    15,         // RIL_DCSLANG_UNKNOWN
    0,          // RIL_DCSLANG_GERMAN
    1,          // RIL_DCSLANG_ENGLISH
    2,          // RIL_DCSLANG_ITALIAN
    3,          // RIL_DCSLANG_FRENSH
    4,          // RIL_DCSLANG_SPANISH
    5,          // RIL_DCSLANG_DUTCH
    6,          // RIL_DCSLANG_SWEDISH
    7,          // RIL_DCSLANG_DANISH
    8,          // RIL_DCSLANG_PORTUGUESE
    9,          // RIL_DCSLANG_FINNISH
    10,         // RIL_DCSLANG_NORWEGIAN
    11,         // RIL_DCSLANG_GREEK
    12,         // RIL_DCSLANG_TURKISH
    13,         // RIL_DCSLANG_HUNGARIAN
    14,         // RIL_DCSLANG_POLISH
    32,         // RIL_DCSLANG_CZECH
};
#define NUM_LANGUAGES   (sizeof(g_rgbBroadcastLangs) / sizeof(BYTE))


//
// Message storage locations
//
static const LPCSTR g_rgszMsgLocations[] =
{
    "",             // RIL_MSGLOC_UNKNOWN
    "BM",           // RIL_MSGLOC_BROADCAST
    "MT",           // RIL_MSGLOC_SIM
    "SR"            // RIL_MSGLOC_STATUSREPORT
};
#define NUM_MSGLOCS     (sizeof(g_rgszMsgLocations) / sizeof(LPCSTR))


//
// Message status values
//
static const DWORD g_rgdwMsgStats[] =
{
    RIL_MSGSTATUS_RECUNREAD,    // 0
    RIL_MSGSTATUS_RECREAD,      // 1
    RIL_MSGSTATUS_STOUNSENT,    // 2
    RIL_MSGSTATUS_STOSENT,      // 3
};
#define NUM_MSGSTATS    (sizeof(g_rgdwMsgStats) / sizeof(DWORD))


// Configures how many retries for sending SMS are done on a MC75. For GCF approval this should be 0,
// but on some networks it is usefull, to do an automatic retry by the RIL. These values can be overwritten
// with registry keys.
#define MC75_SMSRETRY_TRIES     2
#define MC75_SMSRETRY_DELAY     4000

//
// Appends specified GSM broadcast DCS value value to the generated DCS range
//
static void AppendLangString(const LPCSTR szAppend, LPSTR& szWalk, const UINT cbOut, UINT& rcbUsed)
{
    TBD_FUNCTION(AppendLangString);
    UINT cbAppend = strlen(szAppend);

    if ((1 < rcbUsed) && (rcbUsed + 1 <= cbOut))  // Need for and room for ','
    {
        *szWalk = ',';  // Overwrite '\0'
        szWalk++;
        *szWalk = '\0';  // Add '\0' back
        rcbUsed++;  // Caller has already counted the '\0', so just count the ',' here
    }

    if (rcbUsed + cbAppend <= cbOut)
    {
        strncpyz(szWalk, szAppend, cbOut - rcbUsed);
        szWalk += cbAppend;
        rcbUsed += cbAppend;
    }
    else
    {
        TBD_ASSERT(FALSE);
    }
}


//
// Generates a GSM broadcast DCSs range string corresponding to the specified language mask
//
static void MakeBroadcastLangRange(const DWORD dwLanguages, const LPSTR szOut, const UINT cbOut)
{
    TBD_FUNCTION(MakeBroadcastLangRange);
    UINT i;
    LPSTR szWalk = szOut;
    UINT cbUsed = 0;
    char szNumber[3];

    *szWalk = '\0';

    // The empty string is sufficient for RIL_DCSLANG_ALL.

    if (RIL_DCSLANG_ALL != dwLanguages)
    {
        cbUsed = 1;  // Account for the '\0'

        // Iterate through all bits of the mask to see which languages to add
        for (i = 0; i < sizeof(DWORD)*8; i++)
        {
            if ((dwLanguages >> i) & 0x01)
            {
                _itoa(g_rgbBroadcastLangs[i], szNumber, 10);
                TBD_ASSERT('\0' == szNumber[1] || '\0' == szNumber[2]);
                AppendLangString(szNumber, szWalk, cbOut, cbUsed);
            }
        }
    }
}


static void MakeBroadcastMsgIDs(const RILRANGE rgrrRange[], DWORD dwNumRanges, const LPSTR szOut, const UINT cbOut)
{
    LPSTR szWalk = szOut;
    LPSTR szEnd = szOut + cbOut;
    DWORD cbRange = 0;
    char szRange[64];
    char c_szRangeFmt[] = ",%u-%u";
    char c_szSingleFmt[] = ",%u";
    char * szRangeFmt = c_szRangeFmt + 1;
    char * szSingleFmt = c_szSingleFmt + 1;

    for (DWORD dwCurrentRange = 0; dwCurrentRange < dwNumRanges; dwCurrentRange++)
    {
        if (rgrrRange[dwCurrentRange].dwMinValue == rgrrRange[dwCurrentRange].dwMaxValue)
        {
            cbRange = _snprintfz(szRange, 64, szSingleFmt, rgrrRange[dwCurrentRange].dwMinValue);
        }
        else
        {
            cbRange = _snprintfz(szRange, 64, szRangeFmt, rgrrRange[dwCurrentRange].dwMinValue, rgrrRange[dwCurrentRange].dwMaxValue);
        }

        if (szWalk + cbRange < szEnd)
        {
            strcpy(szWalk, szRange);
            szWalk += cbRange;
        }
        else
        {
            break;
        }

        szRangeFmt = c_szRangeFmt;
        szSingleFmt = c_szSingleFmt;
    }

    *szWalk = '\0';
}

//
// Set a flag in the language mask correponding to the
//    specified GSM broadcast DCS value
//
static BOOL AppendLanguage(const UINT nValue, DWORD& rdwLanguages)
{
    TBD_FUNCTION(AppendLanguage);
    UINT i;
    BOOL fRet = FALSE;

    for (i = 0 ; i < NUM_LANGUAGES; i++)
    {
        if (nValue == g_rgbBroadcastLangs[i])
        {
            TBD_ASSERT(i < 32);
            rdwLanguages |= (0x01 << i);
            break;
        }
    }
    return fRet;
}


//
// Parses a range of GSM broadcast DCSs into a language mask
//
static BOOL ParseBroadcastLangRange(LPCSTR szRange, DWORD& rdwLanguages)
{
    TBD_FUNCTION(ParseBroadcastLangRange);
    UINT nValue1;
    UINT nValue2;
    UINT i;
    BOOL fRet = FALSE;

    if (*szRange)
    {
        // Start off with none...
        rdwLanguages = 0;

        while (1)
        {
            if (!ParseUInt(szRange, TRUE, nValue1, szRange))
            {
                goto Error;
            }
            if (MatchStringBeginning(szRange, "-", szRange) &&
                ParseUInt(szRange, TRUE, nValue2, szRange))
            {
                // This is a mini-range
                TBD_ASSERT(nValue1 < nValue2);
                for (i = nValue1; i <= nValue2; i++)
                {
                    (void)AppendLanguage(i, rdwLanguages);
                }
            }
            else
            {
                (void)AppendLanguage(nValue1, rdwLanguages);
            }

            // If there is no trailing comma, we're done
            if (!MatchStringBeginning(szRange, ",", szRange))
            {
                break;
            }
        }
    }
    else
    {
        // An empty string implies all languages.
        rdwLanguages = RIL_DCSLANG_ALL;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

BOOL ParseGetLocation(const LPCSTR szRspOrig, UINT& rnLocation, LPCSTR& rszPointer)
{
    TBD_FUNCTION(ParseGetLocation);
    char szLocation[MAX_ATCMD_LEN];
    UINT nLocation;

    LPCSTR szRsp = szRspOrig;

    // Parse "<xxx_location>"
    if (!ParseString(szRsp, szLocation, MAX_ATCMD_LEN, szRsp))
    {
        return FALSE;
    }

    for (nLocation = NUM_MSGLOCS-1; nLocation > 0; nLocation--)
    {
        if (!strcmp(szLocation, g_rgszMsgLocations[nLocation]))
        {
            break;
        }
    }

    rnLocation = nLocation;
    rszPointer = szRsp;
    return TRUE;
}

static BOOL ParseGetLocationAmount(LPCSTR szRsp, LPCSTR& rszPointer, PUINT pLocation, PUINT pUsed, PUINT pTotal)
{
    TBD_FUNCTION(ParseGetLocationAmount);
    UINT nLocation;
    UINT nUsed;
    UINT nTotal;

    // Go past initial ',' if it's there
    if (*szRsp==',')
    {
        szRsp++;
    }

    // Parse "<xxx_location>"
    if (!ParseGetLocation(szRsp, nLocation, szRsp))
    {
        return FALSE;
    }

    // Parse ",<xxx_used>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nUsed, szRsp))
    {
        return FALSE;
    }

    // Parse ",<xxx_total>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nTotal, szRsp))
    {
        return FALSE;
    }

    *pLocation = nLocation;
    *pUsed = nUsed;
    *pTotal = nTotal;
    rszPointer = szRsp;
    return TRUE;
}

//
//
//
static HRESULT ParseGetMsgServiceOptionsRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetMsgServiceOptionsRsp);
    HRESULT hr = E_FAIL;
    RILMSGSERVICEINFO* prmsi = NULL;
    UINT nValue;
    UINT nLocation;
    UINT nUsed;
    UINT nTotal;

    pBlob = NULL;
    cbBlob = 0;

    prmsi = (RILMSGSERVICEINFO*)AllocBlob(sizeof(RILMSGSERVICEINFO));
    if (!prmsi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmsi, 0x00, sizeof(RILMSGSERVICEINFO));
    prmsi->cbSize = sizeof(RILMSGSERVICEINFO);

    // Parse "<prefix>+CSMS: <svc_type>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSMS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    if (!nValue)
    {
        prmsi->dwService = RIL_MSGSVCTYPE_PHASE2;
    }
    else if (1 == nValue)
    {
        prmsi->dwService = RIL_MSGSVCTYPE_PHASE2PLUS;
    }
    else
    {
        prmsi->dwService = RIL_MSGSVCTYPE_UNKNOWN;
    }
    prmsi->dwParams |= RIL_PARAM_MSI_SERVICE;

    // Parse ",<incoming>"
    prmsi->dwMsgClasses = RIL_MSGCLASS_NONE;
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp))
    {
        goto Error;
    }

    if (1 == nValue)
    {
        prmsi->dwMsgClasses |= RIL_MSGCLASS_INCOMING;
    }

    // Parse ",<outgoing>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp))
    {
        goto Error;
    }

    if (1 == nValue)
    {
        prmsi->dwMsgClasses |= RIL_MSGCLASS_OUTGOING;
    }

    // Parse ",<broadcast><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        goto Error;
    }

    if (1 == nValue)
    {
        prmsi->dwMsgClasses |= RIL_MSGCLASS_BROADCAST;
    }
    prmsi->dwParams |= RIL_PARAM_MSI_MSGCLASSES;

    // Parse "<prefix>+CPMS: "
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPMS: ", szRsp))
    {
        goto Error;
    }

    // Parse Read fields
    if (!ParseGetLocationAmount(szRsp, szRsp, &nLocation, &nUsed, &nTotal))
    {
        goto Error;
    }

    // We use the MT store of the module. This is a combined storage of SIM (SM) and module (ME)
    // storage. Here we are only interessted in the SM part, so we subtract MAX_SMS_IN_ME_STORAGE
    // to report only the
    // Assumption: the ME storage is always empty. This is true, since the user cannot access the
    // ME storage to put SMSs in there.
    prmsi->dwReadLocation = nLocation;
    prmsi->dwReadUsed = min(nUsed, nTotal - MAX_SMS_IN_ME_STORAGE);
    prmsi->dwReadTotal = nTotal - MAX_SMS_IN_ME_STORAGE;
    prmsi->dwParams |= RIL_PARAM_MSI_READLOCATION | RIL_PARAM_MSI_READUSED | RIL_PARAM_MSI_READTOTAL;

    // Note: If write or store fields are missing, we use the values from the last set of fields.
    // Therefore, we don't check the return value from the following two ParseGetLocationAmount calls.
    // This works around Wavecom issues with both versions of Wavecom modules where they don't report
    // all three sets of fields.

    // Parse Write fields
    ParseGetLocationAmount(szRsp, szRsp, &nLocation, &nUsed, &nTotal);
    prmsi->dwWriteLocation = nLocation;
    prmsi->dwReadUsed = min(nUsed, nTotal - MAX_SMS_IN_ME_STORAGE);
    prmsi->dwWriteTotal = nTotal - MAX_SMS_IN_ME_STORAGE;
    prmsi->dwParams |= RIL_PARAM_MSI_WRITELOCATION | RIL_PARAM_MSI_WRITEUSED | RIL_PARAM_MSI_WRITETOTAL;

    // Parse Store fields
    ParseGetLocationAmount(szRsp, szRsp, &nLocation, &nUsed, &nTotal);
    prmsi->dwStoreLocation = nLocation;
    prmsi->dwReadUsed = min(nUsed, nTotal - MAX_SMS_IN_ME_STORAGE);
    prmsi->dwStoreTotal = nTotal - MAX_SMS_IN_ME_STORAGE;
    prmsi->dwParams |= RIL_PARAM_MSI_STORELOCATION | RIL_PARAM_MSI_STOREUSED | RIL_PARAM_MSI_STORETOTAL;

    ParseRspPostfix(szRsp, szRsp);
    pBlob = (void*)prmsi;
    cbBlob = sizeof(RILMSGSERVICEINFO);

    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prmsi);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetMsgServiceOptions(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetMsgServiceOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (!QueueCmd(URC_PORT, pHandle, "AT+CSMS?;+CPMS?\r", CMDOPT_NONE, APIID_GETMSGSERVICEOPTIONS,
                  ParseGetMsgServiceOptionsRsp, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

static HRESULT ParseSMSPhaseConfirmation(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSMSPhaseConfirmation);
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    if (pParam == NULL)
        goto Error;

    // This parsing function is called, when the module has been successfully changed to
    // SMS-Phase 2+. In this case we have to set the AT+CNMI parameters accordingly.

    CreateThread(NULL, 0, CNMIReactivationThreadProc, pParam, 0, NULL);

    hr = S_OK;

    Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetMsgServiceOptions(DWORD dwParam, const RILMSGSERVICEINFO* lpMsgServiceInfo)
{
    TBD_FUNCTION(RILDrv_SetMsgServiceOptions);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    UINT nValue;
    BOOL fStorageSpecified = TRUE;
    RILMSGSTORAGEINFO rmsi; memset(&rmsi,0,sizeof(rmsi)); // zero struct
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT"
    (void)strncpyz(szWalk, "AT", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_SERVICE)
    {
        if (RIL_MSGSVCTYPE_PHASE2 == lpMsgServiceInfo->dwService)
        {
            // Before we are going back to SMS Phase 2, we have to change the settings for AT+CNMI.
            if (!QueueInternalCmdBlocking(URC_PORT, pHandle->GetDevice(), SMS_PHASE2_CNMI, CMDOPT_NONE, APIID_NONE, 1000, 0, 0))
            {
                hr = E_FAIL;
                goto Error;
            }
            nValue = 0;
        }
        else if (RIL_MSGSVCTYPE_PHASE2PLUS == lpMsgServiceInfo->dwService)
        {
            nValue = 1;
        }
        else
        {
            TBD_ASSERT(FALSE);
        }

        // Add "+CSMS=<service>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CSMS=%u\r", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
        if (!QueueCmd(URC_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETMSGSERVICEOPTIONS, (nValue==1?ParseSMSPhaseConfirmation:NULL), NULL, hr, NULL, (nValue==1?pHandle->GetDevice():NULL)))
        {
            hr = E_FAIL;
            goto Error;
        }
#ifdef AUXCOM_PORT
        if (!IS_ADI)
        {
            // This workaround is needed, since the setting of +CSMS in instance specific, but has to
            // consistence for all used instances.
            QueueInternalCmd(COMMAND_PORT, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_NONE, 1000, 0, 0);
        }
#endif
        Sleep(500);
    }

    if ((lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_READLOCATION) ||
        (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_WRITELOCATION) ||
        (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_STORELOCATION))
    {
        // At least one location is specified
        szWalk = szCmd;
        // Add "+CPMS="
        (void)strncpyz(szWalk, "AT+CPMS=", MAX_ATCMD_LEN - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_READLOCATION)
    {
        nValue = lpMsgServiceInfo->dwReadLocation;
        if (RIL_MSGLOC_UNKNOWN == nValue)
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        TBD_ASSERT(NUM_MSGLOCS > nValue);

        // Add "<read_storage>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", g_rgszMsgLocations[nValue]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        rmsi.dwReadLocation = nValue;
        rmsi.dwParams |= RIL_PARAM_MSTI_READLOCATION;
    }
    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_WRITELOCATION)
    {
        // Add ","
        (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        nValue = lpMsgServiceInfo->dwWriteLocation;
        if (RIL_MSGLOC_UNKNOWN == nValue)
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        TBD_ASSERT(NUM_MSGLOCS > nValue);

        // Add "<write_storage>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", g_rgszMsgLocations[nValue]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        rmsi.dwWriteLocation = nValue;
        rmsi.dwParams |= RIL_PARAM_MSTI_WRITELOCATION;
    }

    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_STORELOCATION)
    {
        if ((lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_WRITELOCATION) == 0)
        {
            // Add ","
            (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }

        // Add ","
        (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        nValue = lpMsgServiceInfo->dwStoreLocation;
        if (RIL_MSGLOC_UNKNOWN == nValue)
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        TBD_ASSERT(NUM_MSGLOCS > nValue);

        // Add "<store_storage>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", g_rgszMsgLocations[nValue]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        rmsi.dwStoreLocation = nValue;
        rmsi.dwParams |= RIL_PARAM_MSTI_STORELOCATION;
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

    if (rmsi.dwParams)
    {
        pnd = new CNotificationData;
        if (pnd)
        {
            rmsi.cbSize = sizeof(RILMSGSTORAGEINFO);
            if (!pnd->InitFromRealBlob(RIL_NOTIFY_MSGSTORAGECHANGED, &rmsi, sizeof(RILMSGSTORAGEINFO)))
            {
                delete pnd;
                pnd = NULL;
            }
        }
    }
    if (!QueueCmdWithRetry(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETMSGSERVICEOPTIONS, NULL, pnd, hr, 2, 10000))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

    Error:
    return hr;
}


//
//
//
static HRESULT ParseGetMsgConfigRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetMsgConfigRsp);
    UINT nValue;
    WCHAR wszAddress[MAX_ATCMD_LEN];
    //    char szLanguageRange[MAX_ATCMD_LEN];
    RILMSGCONFIG* prmc = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prmc = (RILMSGCONFIG*)AllocBlob(sizeof(RILMSGCONFIG));
    if (!prmc)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmc, 0x00, sizeof(RILMSGCONFIG));
    prmc->cbSize = sizeof(RILMSGCONFIG);

    // Parse "<prefix>+CSCA: <svcctr_addr>,<type_of_addr><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringBeginning(szRsp, "+CSCA: ", szRsp)              ||
        !ParseUCS2String(szRsp, wszAddress, MAX_ATCMD_LEN, szRsp)        ||
        !MatchStringBeginning(szRsp, ",", szRsp)                    ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 0x100, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Convert data to address structure
    if (!UCS2StringToRILAddress(wszAddress, (BYTE)nValue, prmc->raSvcCtrAddress))
    {
        hr = E_FAIL;
        goto Error;
    }
    prmc->dwParams |= RIL_PARAM_MC_SVCCTRADDRESS;

    pBlob = (void*)prmc;
    cbBlob = sizeof(RILMSGCONFIG);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prmc);
    }
    return hr;

}


//
//
//
HRESULT RILDrv_GetMsgConfig(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetMsgConfig);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmdWithRetry(COMMAND_PORT, pHandle, "AT+CSCA?\r", CMDOPT_NONE, APIID_GETMSGCONFIG, ParseGetMsgConfigRsp, NULL, hr, 2, 10000))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetMsgConfig(DWORD dwParam, const RILMSGCONFIG* lpMsgConfigInfo)
{
    TBD_FUNCTION(RILDrv_SetMsgConfig);
    char szCmd[MAXLENGTH_MSGIDS + MAX_ATCMD_LEN * 3];
    LPSTR szWalk = szCmd;
    WCHAR wszSvcCtrAddress[MAX_ATCMD_LEN];
    char szEncodedSvcCtrAddress[MAX_ATCMD_LEN];
    BYTE bTypeOfAddress;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    (void)strncpyz(szWalk, "AT", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk += 2;

    if (lpMsgConfigInfo->dwParams & RIL_PARAM_MC_SVCCTRADDRESS)
    {
        hr = RILAddressToUCS2String(lpMsgConfigInfo->raSvcCtrAddress, wszSvcCtrAddress, MAX_ATCMD_LEN, bTypeOfAddress);
        if (FAILED(hr))
        {
            goto Error;
        }
        UCS2ToGSMHex(wszSvcCtrAddress, wcslen(wszSvcCtrAddress), szEncodedSvcCtrAddress, sizeof(szEncodedSvcCtrAddress));
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CSCA=\"%s\",%u", szEncodedSvcCtrAddress, bTypeOfAddress);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETMSGCONFIG, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

static RILCBMSGCONFIG * AllocCbMsgConfig(DWORD dwNumRanges)
{
    DWORD dwSize = sizeof(RILCBMSGCONFIG) + dwNumRanges * sizeof(RILRANGE);

    RILCBMSGCONFIG* prmc = (RILCBMSGCONFIG*)AllocBlob(dwSize);
    if (NULL != prmc)
    {
        memset(prmc, 0x00, dwSize);
        prmc->cbSize = dwSize;
    }

    return prmc;
}

//
//
//
static HRESULT ParseBroadcastMsgIDs(const LPCSTR szBroadcastMsgIDs,
                                    RILCBMSGCONFIG ** pprCbMsgConfig)
{
    TBD_ASSERT(NULL != szBroadcastMsgIDs);

    HRESULT hr = S_OK;
    DWORD dwNumRanges;
    LPCSTR szDummy;
    DWORD dwNumIDs = 0;

    if (*szBroadcastMsgIDs)
    {
        dwNumIDs++;

        for (LPCSTR szPtr = szBroadcastMsgIDs; '\0' != *szPtr; szPtr++)
        {
            if (',' == *szPtr)
                dwNumIDs++;
        }
    }

    *pprCbMsgConfig = AllocCbMsgConfig(dwNumIDs);
    if (NULL == *pprCbMsgConfig)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!ParseRangesFromString(szBroadcastMsgIDs,
                               (*pprCbMsgConfig)->rgrrBroadcastMsgIDs,
                               dwNumRanges,
                               dwNumIDs,
                               szDummy))
    {
        hr = E_FAIL;
        goto Error;
    }

    TBD_ASSERT(dwNumRanges == dwNumIDs);

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetCellBroadcastMsgConfigRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCellBroadcastMsgConfigRsp);
    UINT nValue;
    BOOL fAccept = FALSE;
    char szLanguageRange[MAX_ATCMD_LEN];
    char szBroadcastMsgIDs[MAXLENGTH_MSGIDS];
    RILCBMSGCONFIG* prmc = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CSCB: <mode>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSCB: ", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Cache accept value for when we have a structure allocated
    fAccept = (nValue == 0);

    // Parse ",<msg_ids>,<msg_langs><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)                              ||
        !ParseString(szRsp, szBroadcastMsgIDs, MAXLENGTH_MSGIDS, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Parse broadcast message IDs
    // This also allocates the blob
    hr = ParseBroadcastMsgIDs(szBroadcastMsgIDs, &prmc);
    if (FAILED(hr))
    {
        goto Error;
    }

    prmc->fAccept = fAccept;
    prmc->dwParams |= (RIL_PARAM_CBMC_BROADCASTMSGIDS | RIL_PARAM_CBMC_ACCEPTIDS);

    if (MatchStringBeginning(szRsp, ",", szRsp))
    {
        if (!ParseString(szRsp, szLanguageRange, MAX_ATCMD_LEN, szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }

        // Parse accepted languages
        if (!ParseBroadcastLangRange(szLanguageRange, prmc->dwBroadcastMsgLangs)) {
            hr = E_FAIL;
            goto Error;
        }

        prmc->dwParams |= RIL_PARAM_CBMC_BROADCASTMSGLANGS;
    }

    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prmc;
    cbBlob = prmc->cbSize;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prmc);
    }
    return hr;

}

//
//
//
HRESULT RILDrv_GetCellBroadcastMsgConfig(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetCellBroadcastMsgConfig);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmdWithRetry(URC_PORT, pHandle, "AT+CSCB?\r", CMDOPT_NONE, APIID_GETCBMSGCONFIG, ParseGetCellBroadcastMsgConfigRsp, NULL, hr, 3, 3000))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetCellBroadcastMsgConfig(DWORD dwParam, const RILCBMSGCONFIG* lpMsgConfigInfo)
{
    TBD_FUNCTION(RILDrv_SetCellBroadcastMsgConfig);
    char szCmd[MAXLENGTH_MSGIDS + MAX_ATCMD_LEN * 3];
    LPSTR szWalk = szCmd;
    char szCBMsgLangs[MAX_ATCMD_LEN];
    char szBroadcastMsgIDs[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_ACCEPTIDS))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=%d", (lpMsgConfigInfo->fAccept?0:1));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);
    g_fReceiveCellBroadCast = (lpMsgConfigInfo->fAccept?0:1);
    g_szReceiveCellBroadCastChannels[0] = '\0';
    g_szReceiveCellBroadCastDCS[0] = '\0';

    if ((lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGIDS))
    {
        MakeBroadcastMsgIDs(lpMsgConfigInfo->rgrrBroadcastMsgIDs,
                            (lpMsgConfigInfo->cbSize - sizeof(RILCBMSGCONFIG)) / sizeof(RILRANGE),
                            szBroadcastMsgIDs,
                            MAX_ATCMD_LEN);

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN, ",\"%s\"", szBroadcastMsgIDs);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
        if (strlen(szBroadcastMsgIDs) < MAX_CBM_STRING)
            strcpy(g_szReceiveCellBroadCastChannels, szBroadcastMsgIDs);

    }

    if (lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGLANGS)
    {
        MakeBroadcastLangRange(lpMsgConfigInfo->dwBroadcastMsgLangs, szCBMsgLangs, MAX_ATCMD_LEN);

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%s,\"%s\"", (lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGIDS)?"":",", szCBMsgLangs);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
        if (strlen(szCBMsgLangs) < MAX_CBM_STRING)
            strcpy(g_szReceiveCellBroadCastDCS, szCBMsgLangs);
    }

    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\r");

    if (!QueueCmd(URC_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETCBMSGCONFIG, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

//
//
//
static HRESULT ParseReadMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseReadMsg);
    UINT nValue;
    UINT cbMessage;
    BOOL fIncoming;
    RILMESSAGEINFO* prmi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prmi = (RILMESSAGEINFO*)AllocBlob(sizeof(RILMESSAGEINFO));
    if (!prmi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmi, 0x00, sizeof(RILMESSAGEINFO));
    prmi->cbSize = sizeof(RILMESSAGEINFO);
//  +CMGR: 0,,24<cr><lf>0791448720003023040C9144776359085700113011314175140005E17338ED06<cr><lf>0<cr>
    // Parse "<prefix>+CMGR: <stat>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CMGR: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (NUM_MSGSTATS > nValue)
    {
        prmi->dwStatus = g_rgdwMsgStats[nValue];
    }
    else
    {
        prmi->dwStatus = RIL_MSGSTATUS_UNKNOWN;
    }
    prmi->dwParams |= RIL_PARAM_MI_STATUS;

    // Parse",<alpha>" (if present)
    if (!MatchStringBeginning(szRsp, ",", szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Copy alpha to temp buffer & throw away
    TCHAR szBuf[MAXLENGTH_ADDRESS];
    (void)ParseQuotedEncodedString(ENCODING_UCS2, szRsp, szBuf, szBuf + MAXLENGTH_ADDRESS);

    // Parse ",<length><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Calculate the total number of bytes in the message data: (length of the PDU data +
    //    length os SC address (stored in the first 2 bytes) + 1) * 2
    cbMessage = (nValue + 1 + SemiByteCharsToByte(*szRsp, *(szRsp + 1))) * 2;

    // Parse "<pdu>"
    fIncoming = (RIL_MSGSTATUS_RECUNREAD == prmi->dwStatus ||
                 RIL_MSGSTATUS_RECREAD == prmi->dwStatus);
    if (nValue == 0)
    {
        hr = RIL_E_INVALIDINDEX;
        goto Error;
    }
    hr = ParseSMSMessage(szRsp, cbMessage, fIncoming, TRUE, prmi->rmMessage);
    if (FAILED(hr))
    {
        RILRetailTrace((TEXT("RilDrv: Failed to parse SMS message\r\n")));
        goto Error;
    }
    szRsp += cbMessage;
    prmi->dwParams |= RIL_PARAM_MI_MESSAGE;

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // NOTE: there may be more data in the response (if we also deleted the message),
    //       but we don't care about it

    pBlob = (void*)prmi;
    cbBlob = sizeof(RILMESSAGEINFO);

Error:
    if (FAILED(hr))
    {
        FreeBlob(prmi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_ReadMsg(DWORD dwParam, DWORD dwIndex)
{
    TBD_FUNCTION(RILDrv_ReadMsg);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGR=%u\r", dwIndex);
    if (!QueueCmdWithRetry(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_READMSG, ParseReadMsg, pnd, hr, 3, 3000))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;


    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_DeleteMsg(DWORD dwParam, DWORD dwIndex)
{
    TBD_FUNCTION(RILDrv_DeleteMsg);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_MSGDELETED, dwIndex))
    {
        delete pnd;
        pnd = NULL;
    }
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGD=%u\r", dwIndex);
    if (!QueueCmdWithRetry(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_DELETEMSG, NULL, pnd, hr, 3, 3000))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;
    Error:
    return hr;
}


//
//
//
static HRESULT ParseWriteMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseWriteMsg);
    UINT nValue;
    DWORD* pdwIndex = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    pdwIndex = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwIndex)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwIndex, 0x00, sizeof(DWORD));

    // Parse "+CMGW: <index><postfix>"
    if (!MatchStringAnywhere(szRsp, "+CMGW: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        goto Error;
    }

    *pdwIndex = (DWORD)nValue;

    pBlob = (void*)pdwIndex;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwIndex);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_WriteMsg(DWORD dwParam, const RILMESSAGE *lpMessage, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_WriteMsg);
    CNotificationData* pnd = NULL;
    char szCmd[MSG_CMDBUF_LENGTH];
    char szCmd2[MSG_CMDBUF_LENGTH];
    char szMessage[MSG_MSGBUF_LENGTH];
    UINT cbMessage;
    UINT nGSMLength;
    UINT nValue;
    UINT i;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

//  NKDbgPrintfW(TEXT("RilDrv: +RILDrv_WriteMsg()\r\n"));

    hr = EncodeSMSMessage(*lpMessage, szMessage, MSG_MSGBUF_LENGTH - 1, cbMessage, nGSMLength);
    if (FAILED(hr))
    {
        goto Error;
    }
    if (!cbMessage)
    {
        hr = E_FAIL;
        goto Error;
    }
    szMessage[cbMessage] = '\0';

    for (i = 0; i < NUM_MSGSTATS; i++)
    {
        if (dwStatus == g_rgdwMsgStats[i])
        {
            nValue = i;
            break;
        }
    }
    if (NUM_MSGSTATS == i)
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->DelayInitFromRspBlob(RIL_NOTIFY_MSGSTORED))
    {
        delete pnd;
        pnd = NULL;
    }

    (void)_snprintfz(szCmd, MSG_CMDBUF_LENGTH, "AT+CMGW=%u,%u\r", nGSMLength, nValue);
    (void)_snprintfz(szCmd2, MSG_CMDBUF_LENGTH,  "%s\x1a", szMessage);
    if (!QueueMultipartCmd(COMMAND_PORT, pHandle, szCmd, szCmd2, CMDOPT_INTERMEDIATERESPONSE, APIID_WRITEMSG, ParseWriteMsg, pnd, hr))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

    Error:
//  NKDbgPrintfW(TEXT("RilDrv: -RILDrv_WriteMsg()\r\n"));
    return hr;
}


//
//
//
static HRESULT ParseSendMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSendMsg);
    UINT nValue;
    DWORD* pdwMsgRef = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    pdwMsgRef = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMsgRef)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwMsgRef, 0x00, sizeof(DWORD));

    if (!MatchStringAnywhere(szRsp, "+CMGS: ", szRsp) &&
        !MatchStringAnywhere(szRsp, "+CMGC: ", szRsp))
    {
        goto Error;
    }

    // Parse "<msgref><CR><LF>"
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        goto Error;
    }

    g_dwLastSMSCmd = GetTickCount();

    *pdwMsgRef = (DWORD)nValue;

    pBlob = (void*)pdwMsgRef;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwMsgRef);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_SendMsg(DWORD dwParam, const RILMESSAGE *lpMessage, DWORD dwOptions)
{
    TBD_FUNCTION(RILDrv_SendMsg);
    char szCmd[MSG_CMDBUF_LENGTH];
    char szCmd2[MSG_CMDBUF_LENGTH];
    LPCSTR szATCmd;
    char szMessage[MSG_MSGBUF_LENGTH];
    UINT cbMessage;
    UINT nGSMLength;
    HRESULT hr = S_OK;
    DWORD dwRetries, dwRetryDelay;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSRetries"), &dwRetries))
        dwRetries = (IS_ADI?MC75_SMSRETRY_TRIES:0);
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSRetryDelay"), &dwRetryDelay))
        dwRetryDelay =  (IS_ADI?MC75_SMSRETRY_DELAY:0);

    /*NKDbgPrintfW(TEXT("RilDrv: +RILDrv_SendMsg()\r\n"));
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: msg type = 0x%X\r\n"), lpMessage->dwType);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: msg flags = 0x%X\r\n"), lpMessage->dwFlags);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: address = %s\r\n"), lpMessage->msgOutSubmit.raDestAddress.wszAddress);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: protocol = %d\r\n"), lpMessage->msgOutSubmit.dwProtocolID);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: DCS type = %d\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwType);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: DCS flags = 0x%X\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwFlags);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: DCS class = %d\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwMsgClass);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: DCS alphabet = %d\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwAlphabet);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: VPFormat = %d\r\n"), lpMessage->msgOutSubmit.dwVPFormat);
    NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: msg length = %d\r\n"), lpMessage->msgOutSubmit.cchMsgLength);
    UINT i;
    for(i=0;i<(lpMessage->msgOutSubmit.cchMsgLength);i++)
    {
        NKDbgPrintfW(TEXT("RilDrv: RILDrv_SendMsg: msg = 0x%X  %c\r\n"), lpMessage->msgOutSubmit.rgbMsg[i], lpMessage->msgOutSubmit.rgbMsg[i]);
    }

    if(lpMessage->msgOutSubmit.rmdDataCoding.dwAlphabet == RIL_DCSALPHABET_UCS2)NKDbgPrintfW(TEXT("RilDrv: Send UCS2 encoded message\r\n"));*/

    // We can only send outgoing messages
    if (RIL_MSGCLASS_OUTGOING != MSGCLASS_FROM_MSGTYPE(lpMessage->dwType))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    hr = EncodeSMSMessage(*lpMessage, szMessage, MSG_MSGBUF_LENGTH - 1, cbMessage, nGSMLength);
    if (FAILED(hr))
    {
        goto Error;
    }
    if (!cbMessage)
    {
        hr = E_FAIL;
        goto Error;
    }
    szMessage[cbMessage] = '\0';

    if ((RIL_MSGTYPE_OUT_SUBMIT == lpMessage->dwType) || (RIL_MSGTYPE_OUT_RAW == lpMessage->dwType))
    {
        szATCmd = "CMGS";
    }
    else if (RIL_MSGTYPE_OUT_COMMAND == lpMessage->dwType)
    {
        szATCmd = "CMGC";
    }
    else
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Intentionally removed: handling of the RIL_SENDOPT_PERSISTLINK option. This option is
    // no longer needed by the network providers. For this reason the related AT+CMMS command
    // is not part of the MC75, MC55 and AC45 AT-command set.
   (void)_snprintfz(szCmd, MSG_CMDBUF_LENGTH, "AT+%s=%u\r", szATCmd, nGSMLength);

    (void)_snprintfz(szCmd2, MSG_CMDBUF_LENGTH,  "%s\x1a", szMessage);

    // Special problem of all modules: you need a minimum time delay between two SMS related commands,
    // especially between AT+CNMA and AT+CMGS or two AT+CMGS commands.
    if (g_dwLastSMSCmd > 0) {
        DWORD dwCurTime, dwMaxTime = 0;

        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSWaitPeriod"), &dwMaxTime) && dwMaxTime > 0) {
            dwCurTime = GetTickCount();

            if (dwCurTime - g_dwLastSMSCmd < dwMaxTime)
                Sleep(dwMaxTime - (dwCurTime - g_dwLastSMSCmd));
        }
    }
    g_dwLastSMSCmd = GetTickCount();
#ifdef AUXCOM_PORT
    if (!QueueMultipartCmdWithRetry(COMMAND_PORT,
                pHandle, szCmd, szCmd2, CMDOPT_RETRYONSIMLOCKED | CMDOPT_INTERMEDIATERESPONSE,
                APIID_SENDMSG, ParseSendMsg, NULL, hr, NULL, NULL, dwRetries, dwRetryDelay))
#else
    // If we don't have an 3rd port available, we should use the data port to send the command, unless
    // we are in Datamode. This is necessary to pass the GCF SMS collision test.
    if (!QueueMultipartCmdWithRetry(pHandle->GetDevice()->GetComDevice(DATA_PORT)->FDataMode()?COMMAND_PORT:DATA_PORT,
                pHandle, szCmd, szCmd2, CMDOPT_RETRYONSIMLOCKED | CMDOPT_INTERMEDIATERESPONSE,
                APIID_SENDMSG, ParseSendMsg, NULL, hr, NULL, NULL, dwRetries, dwRetryDelay))
#endif
    {
        hr = E_FAIL;
        goto Error;
    }
    Error:
//  NKDbgPrintfW(TEXT("RilDrv: -RILDrv_SendMsg()\r\n"));
    return hr;
}


//
//
//
static HRESULT ParseSendStoredMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSendStoredMsg);
    UINT nValue;
    DWORD* pdwMsgRef = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwMsgRef = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMsgRef)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwMsgRef, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CMSS: <msgref><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CMSS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    *pdwMsgRef = (DWORD)nValue;

    pBlob = (void*)pdwMsgRef;
    cbBlob = sizeof(DWORD);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwMsgRef);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_SendStoredMsg(DWORD dwParam, DWORD dwIndex, DWORD dwOptions)
{
    TBD_FUNCTION(RILDrv_SendStoredMsg);
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMSS=%u\r", dwIndex);

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_RETRYONSIMLOCKED, APIID_SENDSTOREDMSG, ParseSendStoredMsg, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SendMsgAcknowledgement(DWORD dwParam, BOOL fSuccess)
{
    TBD_FUNCTION(RILDrv_SendMsgAcknowledgement);
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CNMA=0\r");

    g_dwLastSMSCmd = GetTickCount();

    if (!QueueCmd(URC_PORT, pHandle, szCmd, CMDOPT_NONE|CMDOPT_SMSACK, APIID_SENDMSGACKNOWLEDGEMENT, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

HRESULT ParseSMSListAndSendAck(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSMSListAndSendAck);
    HRESULT hr;
    UINT nValue;
    RILMESSAGE_IN_SIM rmMessageInSim;
    CRilHandle* pHandle;
    LPCSTR szDummy;

    pHandle = (CRilHandle *)pParam;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, "^SMGL: ", szRsp)) {
        CNotificationData* pnd = NULL;

        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }

        rmMessageInSim.cbSize = sizeof(rmMessageInSim);
        rmMessageInSim.dwParams = RIL_PARAM_MIS_LOCATION | RIL_PARAM_MIS_INDEX;
        rmMessageInSim.dwLocation = RIL_MSGLOC_SIM;
        rmMessageInSim.dwIndex = nValue;

        pnd = new CNotificationData;
        if (pnd!=NULL && pHandle!=NULL)
        {
            pnd->InitFromRealBlob(RIL_NOTIFY_MESSAGE_IN_SIM, &rmMessageInSim, sizeof(RILMESSAGE_IN_SIM));
            pHandle->BroadcastNotification(pnd);
        }

Continue:
        // Find "<postfix>" (skip rest of parameter)
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Find "<postfix>" (skip PDU)
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Sometimes there is an extra <cr><lf> between the lines
        if (MatchStringBeginning(szRsp, "\r\n", szDummy))
            FindRspPostfix(szRsp, szRsp);
    }

Error:
    return hr;
}

//
//
//
DWORD WINAPI CNMIReactivationThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;

    // Problem: when the CNMI was reset by the module all following SMS are stored on the SIM
    // without sending any notifications. This applies only, if the module is in SMS Phase 2+.

    // Step 1: Reactivate the URC:
    QueueInternalCmdBlocking(URC_PORT, pHandle, SMS_PHASE2PLUS_CNMI, CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    // Step 2: Check SIM, if there are any unread SMS:
    QueueInternalCmdBlocking(COMMAND_PORT, pHandle, "AT^SMGL=0\r", CMDOPT_NONE, APIID_QUERYUNREADSMS, 0, 0, 0, ParseSMSListAndSendAck, NULL, pVoid);
    return 0;
}
